package simpledb.file;

import static simpledb.file.Page.BLOCK_SIZE;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * FileMgr class manages the operations involving files on disk.
 * @author Conan
 *
 */
public class FileMgr {
	private String filename;
	private File dbFile;
	private boolean isNew;
	private FileChannel dbFC;

	/**
	 * Create the database file named "filename.mydb" 
	 * under the directory "Database"
	 */
	public FileMgr(String fn) {
		File dir = new File("Database");
		filename = fn;
		filename += ".mydb";
		
   		// create the directory if the database is new
   		if (!dir.exists() && !dir.mkdir())
			throw new RuntimeException("cannot create directory Database");

   		dbFile = new File(dir, filename);
   		isNew = !dbFile.exists();
 
   		if (isNew){
   			try{
   				dbFile.createNewFile();
   			}catch(Exception e){
   				throw new RuntimeException("cannot create " + filename);
   	   		}
   		}

   		try{
   			RandomAccessFile f = new RandomAccessFile(dbFile, "rws");
   			dbFC = f.getChannel();
   		}catch(Exception e){
   			throw new RuntimeException("cannot access " + filename);
   		}
	}

	synchronized void read(int blk, ByteBuffer bb) {
		try {
			bb.clear();
			dbFC.read(bb, blk * BLOCK_SIZE);
		}
		catch (IOException e) {
			throw new RuntimeException("cannot read block " + blk);
		}
	}

	synchronized void write(int blk, ByteBuffer bb) {
		try {
			bb.rewind();
			dbFC.write(bb, blk * BLOCK_SIZE);
		}
		catch (IOException e) {
			throw new RuntimeException("cannot write block" + blk);
		}
	}

	synchronized int append(ByteBuffer bb) {
		int newblk = size();
		write(newblk, bb);
		return newblk;
	}

	synchronized int truncate(){
		if(size() <= 1) return -1;
		try{
			dbFC = dbFC.truncate(BLOCK_SIZE*(size()-1));
			return size();
		}
		catch (IOException e) {
			throw new RuntimeException("cannot truncate " + filename);
		}
	}
	/**
	 * return the database file size in the numbuer of blocks
	 * @return block number
	 */
	public synchronized int size() {
		try {
			return (int)(dbFC.size() / BLOCK_SIZE);
		}
		catch (IOException e) {
			throw new RuntimeException("cannot access " + filename);
		}
	}
	/**
	 * @return whether the file already exists
	 */
	public boolean isNew() {
		return isNew;
	}
}
